/*
 * CPollTimerQueue.cpp
 *
 *  Created on: 2018年5月31日
 *      Author: zhengboyuan
 */

#include "CPollTimerQueue.h"
#include "ClockTime.h"
#include <algorithm>



namespace comn
{

struct PollTimerTaskIsThisID : public std::binary_function<PollTimerTask, int, bool >
{
    bool operator () (const PollTimerTask& task, int id) const
    {
        return (task.m_id == id);
    }
};

struct PollTimerTaskExpireComparator: public std::binary_function< PollTimerTask, PollTimerTask, bool>
{
    bool operator () (const PollTimerTask& first, const PollTimerTask& task) const
    {
        return (first.m_expire < task.m_expire);
    }
};



CPollTimerQueue::CPollTimerQueue():
	m_nextID(1), 
	m_curTid()
{
}

CPollTimerQueue::~CPollTimerQueue()
{
}

int CPollTimerQueue::poll()
{
    int64_t timeout = checkExpire();
    while (timeout == 0)
    {
        timeout = checkExpire();
    }
    return (int)timeout;
}

int CPollTimerQueue::addTimer(uint32_t ms, PollTimerHandler handler)
{
    if (!handler)
    {
        return -1;
    }

	int id = makeID();
	PollTimerTask task(id, handler, ms, true);
    add(task);
    return id;
}

int CPollTimerQueue::addElapse(uint32_t ms, PollTimerHandler handler)
{
    if (!handler)
    {
        return -1;
    }

	int id = makeID();
	PollTimerTask task(id, handler, ms, false);
    add(task);
    return id;
}

bool CPollTimerQueue::cancel(int id)
{
	bool got = remove(id);
	if (!got)
	{
		if (m_curTid == id)
		{
			m_curTid = 0;
		}
	}
	
	return got;
}

void CPollTimerQueue::clear()
{
	std::lock_guard< std::mutex> lock(m_mutex);
    m_taskList.clear();
}


bool CPollTimerQueue::add(PollTimerTask& task)
{
    bool found = remove(task.m_id);

    task.updateExpire(getCurTime());
    insertTask(task);

    return !found;
}

void CPollTimerQueue::insertTask(PollTimerTask& task)
{
	std::lock_guard< std::mutex> lock(m_mutex);
    iterator it = findWithExpire(task.m_expire);
    m_taskList.insert(it, task);
}

bool CPollTimerQueue::runTask(PollTimerTask& task)
{
    if (task.m_handler)
    {
        task.m_handler(task.m_id);
        return true;
    }
    return false;
}

bool CPollTimerQueue::checkoutFront(PollTimerTask& task, int64_t& timeout)
{
    bool found = false;
    timeout = -1;
	std::lock_guard< std::mutex> lock(m_mutex);
    if (!m_taskList.empty())
    {
        int64_t tmNow = getCurTime();
        task = m_taskList.front();
        if (task.m_expire <= (tmNow + DEVIATION))
        {
            m_taskList.pop_front();
            timeout = 0;
            found = true;
        }
        else
        {
            timeout = (task.m_expire - tmNow);
        }
    }
    return found;
}

int64_t CPollTimerQueue::checkExpire()
{
    int64_t timeout = -1;
	PollTimerTask task;
    if (checkoutFront(task, timeout))
    {
		m_curTid = task.m_id;

        bool done = runTask(task);

        if (done && task.m_isRepeat && m_curTid > 0)
        {
            int64_t expire = task.updateExpire();
            if (expire < getCurTime())
            {
                task.updateExpire(getCurTime());
            }

            insertTask(task);
        }

        timeout = 0;

		m_curTid = 0;
    }
    return timeout;
}


CPollTimerQueue::iterator CPollTimerQueue::find(int id)
{
    return std::find_if(m_taskList.begin(), m_taskList.end(),
            std::bind2nd(PollTimerTaskIsThisID(), id));
}

bool CPollTimerQueue::remove(int id)
{
	std::lock_guard< std::mutex> lock(m_mutex);

    bool found = false;
    iterator it = find(id);
    if (it != m_taskList.end())
    {
        m_taskList.erase(it);
        found = true;
    }
    return found;
}

CPollTimerQueue::iterator CPollTimerQueue::findWithExpire(int64_t expire)
{
	PollTimerTask first;
    first.m_expire = expire;
    return std::upper_bound(m_taskList.begin(), m_taskList.end(),
            first, PollTimerTaskExpireComparator());
}

int64_t CPollTimerQueue::getCurTime()
{
    return comn::ClockTime::getTime();
}

int CPollTimerQueue::makeID()
{
	int newID = m_nextID++;
	while (contains(newID))
	{
		newID = m_nextID++;
	}
	return newID;
}

bool CPollTimerQueue::contains(int id)
{
	std::lock_guard< std::mutex> lock(m_mutex);
	return find(id) != m_taskList.end();
}



} /* namespace util */
